// #include <chrono>
#include <coroutine>
#include "../co_async/steps/debug.hpp"

/**
    所有伺候标准库的函数都用下划线分割的命名法
    所有我们自己的函数都使用驼峰命名
    所有自己的类都是大写开头的。
*/
// struct SuspendAlways // awaiter(有三个函数叫这个， awaiter一定是awaitable，但反过来不一定) / awaitable
//   {
//     constexpr bool await_ready() const noexcept { return false; }

//     constexpr void await_suspend(std::coroutine_handle<> coroutine) const noexcept {}

//     constexpr void await_resume() const noexcept {}
//   };

// awaitable可以转换成awaiter (看起来相当于智能指针 void *operator->(){return get();})
// struct RepeatAwaitable{
//     SuspendAlways operator co_await() {
//         return SuspendAlways();
//     }
// };

// 当父协程mPrevious调用coroutine时，当coroutine挂起，会尝试恢复到mPrevious
struct PreviousAwaiter {
    // 用于记录父协程句柄，以便在当前协程挂起时，恢复到父协程
    std::coroutine_handle<> mPrevious = nullptr;

    constexpr bool await_ready() const noexcept { return false; }

    constexpr std::coroutine_handle<> await_suspend(std::coroutine_handle<> coroutine) const noexcept {
        if(mPrevious){
            return mPrevious;
        }
        else{
            return std::noop_coroutine();
        }
    }

    constexpr void await_resume() const noexcept {}
};

/**
    那么我们说awaiter有什么用呢？
    有时候会有一个协程调用另一个协程的情况
*/

struct Promise {
    /**
    调用时机：协程帧创建后，但在执行协程函数体代码​​之前​
    作用：
        决定协程是否在创建后立即挂起
        返回 std::suspend_always 表示协程创建后​​立即挂起​​（惰性启动）
        返回 std::suspend_never 表示协程创建后​​立即执行​
        当然也可以像上面那样自定义一个suspendxx
    意义：
        允许外部控制何时开始执行协程
        支持异步任务启动模式
    */
    auto initial_suspend() {
        // 进入以后不会立刻执行，而是挂起
        return std::suspend_always();
    }

    /**
    调用时机​​：协程函数体执行完毕（co_return 后）或抛出未捕获异常后，​​在协程帧销毁前​​
    作用​​：
        决定协程结束前是否需要挂起
        通常返回一个 awaitable 对象（此处是自定义的 PreviousAwaiter）
        noexcept 确保异常不会在此处抛出
    关键点​​：
        若返回挂起状态，协程帧不会自动销毁（需手动销毁）
        常用来返回控制权给调用者或父协程
    */
    auto final_suspend() noexcept {
        // 返回控制权到父协程
        return PreviousAwaiter(mPrevious);
    }

    /**
    调用时机​​：协程体内抛出异常且​​未被捕获​​时
    作用​​：
        处理未捕获异常（此处重新抛出）
        可在此处存储异常（如 std::current_exception()）

        void unhandled_exception() {
            exception_ptr = std::current_exception();  // 存储异常
        }
    */
    void unhandled_exception() {
        throw;
    }
    
    // co_return; 写法时候，必须定义该函数
    void return_void() {
        mRetValue=-1;
    }

    // co_return num; 这种写法时，必须定义该函数。与return_void函数冲突
    // void return_value(int ret) {
    //     mRetValue = ret;
    // }

    // co_yield num; 这种写法时，必须定义该函数。
    auto yield_value(int ret) {
        mRetValue = ret;
        return std::suspend_always();
        // return SuspendReturnCoroutine(mPrevious);
    }

    /**
    调用时机​​：在协程创建时，​​在 initial_suspend() 之前​​
    ​作用​​：
        创建并返回协程的外在句柄（用于操作协程）
        通常返回包含 coroutine_handle 的包装对象
    重要​​：
        这是连接 promise 和协程外部接口的桥梁
    */
    std::coroutine_handle<Promise> get_return_object() {
        // debug(), "get_return_object";
        return std::coroutine_handle<Promise>::from_promise(*this);
    }

    // 返回值
    int mRetValue;
    // 父调用者协程的句柄
    std::coroutine_handle<> mPrevious = nullptr;
};

struct Task{
    using promise_type = Promise;

    using BabyType = int;

    // 为什么说根据“三五法则”就要写这一行？禁用移动构造？
    Task(Task &&) = delete;

    ~Task() {
        mCoroutine.destroy();
    }

    Task(std::coroutine_handle<promise_type> coroutine)
        : mCoroutine(coroutine){}

    std::coroutine_handle<promise_type> mCoroutine;
};

struct WorldTask{
    using promise_type = Promise;

    using BabyType = int;

    WorldTask(std::coroutine_handle<promise_type> coroutine)
        : mCoroutine(coroutine){}

    // 为什么说根据“三无法则”就要写这一行？移动构造？
    WorldTask(WorldTask &&) = delete;

    ~WorldTask() {
        mCoroutine.destroy();
    }

    struct WorldAwaiter{
        bool await_ready() const noexcept { return false; }

        std::coroutine_handle<> await_suspend(std::coroutine_handle<> coroutine) const noexcept {
            // 这里设置world返回地址是当前我们正在调用我们的函数，也就是world的外层函数“hello”
            // 所以coroutine其实是调用者。
            // 设置了world返回地址后，world协程执行结束之后就会避免直接返回到main，而是返回到指定的位置。
            mCoroutine.promise().mPrevious = coroutine;

            return mCoroutine;
        }

        void await_resume() const noexcept {}

        std::coroutine_handle<promise_type> mCoroutine;
    };

    auto operator co_await() {
        return WorldAwaiter(mCoroutine);
    }

    std::coroutine_handle<promise_type> mCoroutine;
};

WorldTask world() {
    debug(), "world";
    co_yield 14;
    co_yield 24;
    co_return;
}

Task hello() {
    debug(), "hello:  begin to execute hello";
    WorldTask worldTask = world();
    debug(), "hello:  had build world task";
    // 所以这里的co_await其实类似于main函数中的resume()
    co_await worldTask;
    debug(), "hello得到world返回", worldTask.mCoroutine.promise().mRetValue;
    co_await worldTask;
    debug(), "hello得到world返回", worldTask.mCoroutine.promise().mRetValue;
    debug(), "hello:  had ran world task";
    debug(), "hello:  begin";
    debug(), "hello:  42";
    co_yield 42;
    debug(), "hello:  12";
    co_yield 12;
    debug(), "hello:  6";
    co_yield 6;
    debug(), "hello:  end";
    co_return;
}

int main(){
    debug(), "main:  即将调用hello";
    Task t = hello();
    debug(), "main:  调用完了hello";
    while(!t.mCoroutine.done()){
        t.mCoroutine.resume();
        debug(), "main: main得到的hello结果为", t.mCoroutine.promise().mRetValue;
    }
    return 0;
}